import java.awt.CardLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.Timer;


public class GuiFrame extends JFrame implements FTPCallback, WindowListener {

	private static final long serialVersionUID = -6612636637104441445L;

	private FTPConnection ftp;
 
	private CardLayout layout;
	private JPanel contentPane;

	private JPanel mainPanel;
	private JLabel lblTitle;
	private JButton btnConnect;
	private JButton btnPWD;
	private JButton btnCWD;
	private JButton btnUpDirectory;
	private JButton btnRETR;
	private JButton btnSTOR;
	private JButton btnLIST;
	private JButton btnQUIT;
	private JScrollPane scrollTxtOutput;
	private JTextArea txtOutput;
	private JScrollPane scrollFileList;
	private JList listFileNames;
	private JLabel lblProgressBar;
	private JProgressBar progressBar;

	private JPanel userPromptPanel;
	private JLabel lblServerAddress;
	private JTextField txtServerAddress;
	private JLabel lblUsername;
	private JTextField txtUsername;
	private JLabel lblPassword;
	private JPasswordField txtPassword;
	private JButton btnClear;
	private JButton btnOk;

	private JPanel changeUserPanel;
	private JLabel lblCUUsername;
	private JTextField txtCUUsername;
	private JLabel lblCUPassword;
	private JPasswordField txtCUPassword;

	private JFileChooser fileChooser;

	final private String USER_PROMPT = "userPrompt";
	final private String MAIN = "main";
	final private String WINDOW_TITLE = "FTP Client";
	final private Pattern parseListOutput = Pattern.compile("([-d])[-wrxs]{9}\\s+(.*)\\s+(\\d{2}\\:\\d{2}|\\d{4})\\s+(.*)");
	private Matcher listOutputMatcher;
	private boolean closing;

	private DefaultListModel fileListModel;
	private DefaultListModel dirListModel;




	public static void main(String[] args){
		GuiFrame g = new GuiFrame();
		g.setVisible(true);
	}



	public GuiFrame() {
		setTitle(WINDOW_TITLE);
		fileChooser = new JFileChooser(".");
		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		ftp = new FTPConnection(this);
		layout =  new CardLayout();
		contentPane = new JPanel(layout);
		contentPane.add(getUserPromptPanel(),USER_PROMPT);
		contentPane.add(getMainPanel(),MAIN);
		setContentPane(contentPane);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		addWindowListener(this);
		fileListModel = new DefaultListModel();
		dirListModel = new DefaultListModel();
		pack();
	}

	private JPanel getChangeUserPanel() {
		if(changeUserPanel == null) {
			changeUserPanel = new JPanel(new GridBagLayout());
			GridBagConstraints c  = new GridBagConstraints();

			c.anchor = GridBagConstraints.CENTER;
			c.weightx = 0;
			c.weighty = 0;
			c.fill = GridBagConstraints.HORIZONTAL;

			lblCUUsername = new JLabel("Username:");
			c.gridx = 0;
			c.gridy = 0;
			changeUserPanel.add(lblCUUsername,c);

			lblCUPassword = new JLabel("Password:");
			c.gridx = 0;
			c.gridy = 1;
			changeUserPanel.add(lblCUPassword,c);

			c.insets.right = 0;

			txtCUUsername = new JTextField(20);
			c.gridx = 1;
			c.gridy = 0;
			c.gridwidth = 2;
			changeUserPanel.add(txtCUUsername,c);

			txtCUPassword = new JPasswordField(20);
			c.gridx = 1;
			c.gridy = 1;
			c.gridwidth = 2;
			changeUserPanel.add(txtCUPassword,c);
		}

		return changeUserPanel;
	}

	private JPanel getUserPromptPanel() {
		if(userPromptPanel == null) {
			userPromptPanel = new JPanel(new GridBagLayout());
			GridBagConstraints c  = new GridBagConstraints();

			c.anchor = GridBagConstraints.CENTER;
			c.weightx = 0;
			c.weighty = 0;
			c.fill = GridBagConstraints.HORIZONTAL;

			c.insets.right = 10;
			lblServerAddress = new JLabel("Server Address:");
			c.gridx = 0;
			c.gridy = 0;
			userPromptPanel.add(lblServerAddress,c);

			lblUsername = new JLabel("Username:");
			c.gridx = 0;
			c.gridy = 1;
			userPromptPanel.add(lblUsername,c);

			lblPassword = new JLabel("Password:");
			c.gridx = 0;
			c.gridy = 2;
			userPromptPanel.add(lblPassword,c);

			c.insets.right = 0;
			txtServerAddress = new JTextField(20);
			c.gridx = 1;
			c.gridy = 0;
			c.gridwidth = 2;
			userPromptPanel.add(txtServerAddress,c);

			txtUsername = new JTextField(20);
			c.gridx = 1;
			c.gridy = 1;
			c.gridwidth = 2;
			userPromptPanel.add(txtUsername,c);

			txtPassword = new JPasswordField(20);
			c.gridx = 1;
			c.gridy = 2;
			c.gridwidth = 2;
			userPromptPanel.add(txtPassword,c);

			c.insets.top = 10;
			c.gridx = 0;
			c.gridy = 3;
			c.gridwidth  = 1;
			userPromptPanel.add(getBtnClear(),c);

			c.gridx = 2;
			c.gridy = 3;
			c.gridwidth = 1;
			userPromptPanel.add(getBtnOk(),c);

		}
		return userPromptPanel;
	}

	private JButton getBtnClear() {
		if(btnClear == null) {
			btnClear = new JButton("Clear");
			btnClear.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					txtServerAddress.setText("");
					txtUsername.setText("");
					txtPassword.setText("");
				}
			});
		}
		return btnClear;
	}

	private JButton getBtnOk() {
		if(btnOk == null) {
			btnOk = new JButton("OK");
			btnOk.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					enableLoginPanel(false);
					if(ftp.isConnected()) {
						ftp.authenticate(
								txtUsername.getText(),
								new String(txtPassword.getPassword())
						);
					}
					else {
						ftp.connect(
								txtServerAddress.getText(),
								txtUsername.getText(),
								new String(txtPassword.getPassword())
						);
					}
				}
			});
		}
		return btnOk;
	}
	
	private void enableLoginPanel(boolean enable) {
		txtServerAddress.setEnabled(enable);
		txtUsername.setEnabled(enable);
		txtPassword.setEnabled(enable);
		btnOk.setEnabled(enable);
		btnClear.setEnabled(enable);
	}

	private JPanel getMainPanel(){
		if(mainPanel == null) {
			mainPanel = new JPanel(new GridBagLayout());
			GridBagConstraints c  = new GridBagConstraints();
			c.fill = GridBagConstraints.HORIZONTAL;
			c.gridx = 0;

			c.weighty = 0;
			c.weightx = 0.5;
			c.insets.top = 5;
			c.insets.left = 10;

			c.gridy = 0;
			c.gridx = 0;
			c.gridwidth = 6;
			mainPanel.add(getLblTitle(),c);

			c.gridy = 1;
			c.gridx = 0;
			c.gridwidth = 2;
			c.weighty = 0;
			mainPanel.add(getBtnConnect(),c);
			
			c.gridy = 2;
			c.gridx = 0;
			c.gridwidth = 2;
			c.weighty = 0;
			mainPanel.add(getBtnUpDirectory(),c);

			c.gridy = 3;
			c.gridx = 0;
			c.gridwidth = 1;
			c.gridheight = 2;
			mainPanel.add(getBtnPWD(),c);

			c.gridy = 3;
			c.gridx = 1;
			c.gridwidth = 1;
			c.gridheight= 2;
			mainPanel.add(getBtnCWD(),c);


			c.gridy = 5;
			c.gridx = 0;
			c.gridwidth = 1;
			c.gridheight = 1;
			mainPanel.add(getBtnRETR(),c);

			c.gridy = 5;
			c.gridx = 1;
			c.gridwidth = 1;
			c.gridheight = 1;
			mainPanel.add(getBtnSTOR(),c);

			c.gridy = 6;
			c.gridx = 0;
			c.gridwidth = 1;
			mainPanel.add(getBtnLIST(),c);

			c.gridy = 6;
			c.gridx = 1;
			c.gridwidth = 1;
			mainPanel.add(getBtnQUIT(),c);

			c.gridy = 7;
			c.gridx = 0;
			c.gridwidth = 2;
			mainPanel.add(getLblProgressBar(),c);

			c.gridy = 8;
			c.gridx = 0;
			c.gridwidth = 2;
			mainPanel.add(getProgressBar(),c);

			c.weighty = 1;
			c.weightx = 1;
			c.gridx = 0;
			c.gridy = 9;
			c.gridwidth = 2;
			c.gridheight = 2;
			c.insets.bottom = 5;
			c.fill = GridBagConstraints.BOTH;
			mainPanel.add(getScrollFileList(),c);

			c.weighty = 1;
			c.weightx = 1;
			c.gridy = 1;
			c.gridx = 2;
			c.gridwidth = 2;
			c.gridheight = 10;
			c.ipadx = 450;
			c.insets.bottom = 5;
			c.fill = GridBagConstraints.BOTH;
			mainPanel.add(getScrollTxtOutput(),c);



		}
		return mainPanel;
	}

	/**
	 * @return the lblTitle
	 */
	private JLabel getLblTitle() {
		if(lblTitle == null) {
			lblTitle = new JLabel("Tiny Client");
			lblTitle.setFont(lblTitle.getFont().deriveFont((float)40));
			lblTitle.setHorizontalAlignment(SwingConstants.CENTER);
		}
		return lblTitle;
	}

	/**
	 * @return the btnConnect
	 */
	private JButton getBtnConnect() {
		if(btnConnect == null) {
			btnConnect = new JButton("Change User");
			btnConnect.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					showAuthenticationDialog();
				}
			});
		}
		return btnConnect;
	}

	/**
	 * @return the btnPWD
	 */
	private JButton getBtnPWD() {
		if(btnPWD==null) {
			btnPWD = new JButton("<html>Current<br/>Directory</html>");
			btnPWD.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					ftp.pwd();
				}
			});
		}
		return btnPWD;
	}

	/**
	 * @return the btnCWD
	 */
	private JButton getBtnCWD() {
		if(btnCWD == null) {
			btnCWD = new JButton("<html>Change<br/>Directory</html>");
			btnCWD.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					listFileNames.setModel(dirListModel);
					showListBox(true);
				}
			});
		}
		return btnCWD;
	}
	
	private JButton getBtnUpDirectory() {
		if(btnUpDirectory == null) {
			btnUpDirectory = new JButton("Go Up One Directory");
			btnUpDirectory.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					ftp.cwd("..");
				}
			});
		}
		return btnUpDirectory;
	}

	/**
	 * @return the btnRETR
	 */
	private JButton getBtnRETR() {
		if(btnRETR == null) {
			btnRETR = new JButton("Get File");
			btnRETR.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					listFileNames.setModel(fileListModel);
					showListBox(true);
				}
			});
		}
		return btnRETR;
	}

	/**
	 * @return the btnSTOR
	 */
	private JButton getBtnSTOR() {
		if(btnSTOR == null) {
			btnSTOR = new JButton("Put File");
			btnSTOR.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					fileChooser.showOpenDialog(contentPane);
					File f = fileChooser.getSelectedFile();
					if(f!=null) {
						progressBar.setMaximum((int)f.length());
						ftp.stor(f);
					}
				}
			});
		}
		return btnSTOR;
	}

	/**
	 * @return the btnLIST
	 */
	private JButton getBtnLIST() {
		if(btnLIST == null) {
			btnLIST = new JButton("List");
			btnLIST.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					setAllButtonsDisabled();
					ftp.list();
				}
			});
		}
		return btnLIST;
	}

	/**
	 * @return the btnQUIT
	 */
	private JButton getBtnQUIT() {
		if(btnQUIT == null) {
			btnQUIT= new JButton("Quit");
			btnQUIT.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					ftp.disconnect();
					
				}
			});
		}
		return btnQUIT;
	}

	/**
	 * @return the scrollTxtOutput
	 */
	private JScrollPane getScrollTxtOutput() {
		if(scrollTxtOutput == null) {
			scrollTxtOutput = new JScrollPane(getTxtOutput());
			scrollTxtOutput.setAutoscrolls(true);

			scrollTxtOutput.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		}
		return scrollTxtOutput;
	}

	/**
	 * @return the txtOutput
	 */
	private JTextArea getTxtOutput() {
		if(txtOutput == null) {
			txtOutput = new JTextArea();
			txtOutput.setEditable(false);
		}
		return txtOutput;
	}

	private JScrollPane getScrollFileList() {
		if(scrollFileList == null) {
			scrollFileList = new JScrollPane(getListFileNames());
		}
		return scrollFileList;
	}

	private JList getListFileNames() {
		if(listFileNames == null) {
			listFileNames = new JList();
			listFileNames.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					if(e.getClickCount()==2) {
						String selectedValue = (String)listFileNames.getSelectedValue();
						if(listFileNames.getModel() == dirListModel) {
							ftp.cwd(selectedValue);
						} else if (listFileNames.getModel() == fileListModel) {
							ftp.size(selectedValue);
							ftp.retr(selectedValue);
						}
					}
				}
			});
		}
		return listFileNames;
	}

	private JLabel getLblProgressBar() {
		if(lblProgressBar == null) {
			lblProgressBar = new JLabel("Progress:");

		}
		return lblProgressBar;
	}

	private JProgressBar getProgressBar() {
		if(progressBar == null) {
			progressBar =  new JProgressBar(0, 100000);
		}
		return progressBar;
	}




	private void showAuthenticationDialog() {
		int input = JOptionPane.showOptionDialog(
				this,
				getChangeUserPanel(),
				WINDOW_TITLE,
				JOptionPane.OK_CANCEL_OPTION,
				JOptionPane.PLAIN_MESSAGE,
				null, null,
				0
		);
	
		if(input == JOptionPane.OK_OPTION) {
			ftp.authenticate(txtCUUsername.getText(), new String(txtCUPassword.getPassword()));
		} else if(userPromptPanel.isVisible()) {
			ftp.disconnect();
			ftp = new FTPConnection(this);
			enableLoginPanel(true);
		}
		txtCUPassword.setText("");
		txtCUUsername.setText("");
	}



	private void showProgressBar(boolean show, String text) {
		lblProgressBar.setVisible(show);
		progressBar.setVisible(show);
		progressBar.setStringPainted(true);
		progressBar.setString(text);
		if(!show) {
			progressBar.setValue(0);
		}
	}



	private void showListBox(boolean show){
		scrollFileList.setVisible(show);
		mainPanel.doLayout();
		scrollTxtOutput.doLayout();
		scrollTxtOutput.getViewport().doLayout();
	}



	private void appendToTxtOutput(String s) {
		txtOutput.append(s);
		txtOutput.append("\n");
		txtOutput.setCaretPosition(txtOutput.getDocument().getLength());
	}



	private void populateListBoxes(String raw) {
		if(listOutputMatcher == null) listOutputMatcher = parseListOutput.matcher(raw);
		else listOutputMatcher.reset(raw);
		if(listOutputMatcher.matches()) {
			String typeChar = listOutputMatcher.group(1);
			String fileName = listOutputMatcher.group(4);
			System.out.println(fileName);
			if(typeChar.equals("-")) {
				fileListModel.addElement(fileName);
			} else if(typeChar.equals("d")) {
				dirListModel.addElement(fileName);
			}
	
		}
	}



	@Override
	public void serverTextRecieved(String s) {
		appendToTxtOutput(s);
	}



	@Override
	public void connected(boolean isConnected) {
		if(isConnected) {
			
		} else {
			if(closing) {
				System.exit(0);
			} else {
				ftp = new FTPConnection(this);
				layout.show(contentPane,USER_PROMPT);
				enableLoginPanel(true);
				JOptionPane.showMessageDialog(this, "The connection has been severed.\nPlease login again.");
			} 
		}
	}



	@Override
	public void authenticationEnd(boolean authenticateSuccess) {
		if(authenticateSuccess) {
			showProgressBar(false,null);
			showListBox(false);
			layout.show(contentPane,MAIN);
			ftp.pwd();
			ftp.list();
		} else {
			btnOk.setText("Login failed. Try Again!");
		}
	}

	@Override
	public void requiresAuthentication() {
		showAuthenticationDialog();
	}
	@Override
	public void pwdEnd(String workingDirectory) {
		setTitle(WINDOW_TITLE+": "+workingDirectory);
	}



	@Override
	public void cwdEnd(boolean cwdSuccess) {
		if(cwdSuccess) {
			ftp.pwd();
			ftp.list();
		}
		setAllButtonsEnabled();
	}

	@Override
	public void listEnd(boolean listSuccess) {
		setAllButtonsEnabled();
		if(!listSuccess) connected(false);
	}

	@Override
	public void listLineRecieved(String s) {
		setAllButtonsEnabled();
		appendToTxtOutput("\t"+s);
		populateListBoxes(s);
		
	}

	@Override
	public void listStarted() {
		setAllButtonsDisabled();
		fileListModel.clear();
		dirListModel.clear();
		
	}

	@Override
	public void sizeEnd(int size) {
		if(size > 0) {
			progressBar.setMaximum(size);
		}
		setAllButtonsEnabled();
	}



	@Override
	public void retrStarted(String fileName) {
		setAllButtonsDisabled();
		showProgressBar(true, "Downloading "+fileName);
		
	}
	@Override
	public void retrWriting(int byteCount) {
		setAllButtonsDisabled();
		progressBar.setValue(progressBar.getValue() + byteCount);
	}



	@Override
	public void retrEnd(boolean retrSuccess) {
		setAllButtonsEnabled();
		showProgressBar(false, null);
		if(!retrSuccess) connected(false);
	}

	@Override
	public void storStarted(String fileName) {
		setAllButtonsDisabled();
		showProgressBar(true,"Uploading "+fileName);
	}

	@Override
	public void storWriting(int byteCount) {
		setAllButtonsDisabled();
		progressBar.setValue(progressBar.getValue() + byteCount);
	}



	@Override
	public void storEnd(boolean storSuccess) {
		setAllButtonsEnabled();
		showProgressBar(false,null);
		if(!storSuccess) connected(false);
	}

	@Override
	public void windowClosing(WindowEvent e) {
		closing = true;
		if(ftp!=null) {
			if(ftp.isConnected()) {
				ftp.disconnect();
			} else {
				System.exit(0);
			}
		}
	}




	@Override
	public void windowDeactivated(WindowEvent e) {
		// TODO Auto-generated method stub

	}




	@Override
	public void windowDeiconified(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowIconified(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowOpened(WindowEvent e) {
		// TODO Auto-generated method stub

	}



	@Override
	public void windowActivated(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void windowClosed(WindowEvent e) {
	}

	private void setAllButtonsDisabled() {
		btnConnect.setEnabled(false);
		btnCWD.setEnabled(false);
		btnLIST.setEnabled(false);
		btnPWD.setEnabled(false);
		btnQUIT.setEnabled(false);
		btnRETR.setEnabled(false);
		btnSTOR.setEnabled(false);
		btnUpDirectory.setEnabled(false);
	}
	
	private void setAllButtonsEnabled() {
		btnConnect.setEnabled(true);
		btnCWD.setEnabled(true);
		btnLIST.setEnabled(true);
		btnPWD.setEnabled(true);
		btnQUIT.setEnabled(true);
		btnRETR.setEnabled(true);
		btnSTOR.setEnabled(true);
		btnUpDirectory.setEnabled(true);
	}



	@Override
	public void epsvEnd(boolean epsvSuccess) {
		setAllButtonsEnabled();
	}


}
